import { NextFunction, Request, Response } from 'express'
import { AppError } from '../utils/AppError'

type ValidationRule = {
  type: 'string' | 'number' | 'boolean' | 'array' | 'object' | 'email' | 'url'
  required?: boolean
  min?: number
  max?: number
  pattern?: RegExp
  enum?: any[]
  custom?: (value: any) => boolean
  message?: string
}

type ValidationSchema = {
  [key: string]: ValidationRule
}

// 验证器工具函数
const validators = {
  string: (value: any): value is string => typeof value === 'string',
  number: (value: any): value is number => typeof value === 'number' && !isNaN(value),
  boolean: (value: any): value is boolean => typeof value === 'boolean',
  array: (value: any): value is any[] => Array.isArray(value),
  object: (value: any): value is object =>
    typeof value === 'object' && value !== null && !Array.isArray(value),
  email: (value: any): boolean => {
    const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
    return typeof value === 'string' && emailRegex.test(value)
  },
  url: (value: any): boolean => {
    try {
      new URL(value)
      return true
    } catch {
      return false
    }
  },
}

// 创建验证中间件
export const createValidator = (schema: ValidationSchema) => {
  return (req: Request, _res: Response, next: NextFunction) => {
    const errors: string[] = []

    for (const [field, rules] of Object.entries(schema)) {
      const value = req.body[field]

      // 检查必填字段
      if (rules.required && (value === undefined || value === null || value === '')) {
        errors.push(`${field} is required`)
        continue
      }

      // 如果字段不是必填且值为空，跳过其他验证
      if (!rules.required && (value === undefined || value === null || value === '')) {
        continue
      }

      // 类型验证
      if (!validators[rules.type](value)) {
        errors.push(rules.message || `${field} must be a valid ${rules.type}`)
        continue
      }

      // 长度/大小验证
      if (rules.min !== undefined) {
        if (
          (rules.type === 'string' && value.length < rules.min) ||
          (rules.type === 'number' && value < rules.min) ||
          (rules.type === 'array' && value.length < rules.min)
        ) {
          errors.push(
            `${field} must be at least ${rules.min} ${
              rules.type === 'number' ? '' : 'characters'
            } long`
          )
        }
      }

      if (rules.max !== undefined) {
        if (
          (rules.type === 'string' && value.length > rules.max) ||
          (rules.type === 'number' && value > rules.max) ||
          (rules.type === 'array' && value.length > rules.max)
        ) {
          errors.push(
            `${field} must be at most ${rules.max} ${
              rules.type === 'number' ? '' : 'characters'
            } long`
          )
        }
      }

      // 正则表达式验证
      if (rules.pattern && !rules.pattern.test(value)) {
        errors.push(rules.message || `${field} format is invalid`)
      }

      // 枚举值验证
      if (rules.enum && !rules.enum.includes(value)) {
        errors.push(`${field} must be one of: ${rules.enum.join(', ')}`)
      }

      // 自定义验证
      if (rules.custom && !rules.custom(value)) {
        errors.push(rules.message || `${field} validation failed`)
      }
    }

    if (errors.length > 0) {
      return next(new AppError(400, errors.join('; ')))
    }

    next()
  }
}

// 预定义的验证规则
export const commonValidators = {
  username: {
    type: 'string' as const,
    required: true,
    min: 3,
    max: 20,
    pattern: /^[a-zA-Z0-9_-]+$/,
    message:
      'Username must be 3-20 characters and can only contain letters, numbers, underscores and hyphens',
  },
  password: {
    type: 'string' as const,
    required: true,
    min: 6,
    pattern: /^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d@$!%*#?&]{6,}$/,
    message:
      'Password must be at least 6 characters and contain at least one letter and one number',
  },
  email: {
    type: 'email' as const,
    required: true,
    message: 'Please provide a valid email address',
  },
}

// 使用示例：
/*
const loginValidator = createValidator({
  username: commonValidators.username,
  password: commonValidators.password
});

router.post('/login', loginValidator, loginController);
*/
